package org.zht.dormpro.dorm.cacher.service.impl;

import lombok.extern.log4j.Log4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.zht.dormpro.dorm.cacher.service.IRedisTemplateService;
import org.zht.dormpro.dorm.cacher.service.RedisTemplateCallBack;
import org.zht.dormpro.pub.util.KryoUtil;

import java.util.*;

@Service
@Log4j
public class RedisTemplateServiceImpl implements IRedisTemplateService {
    @Autowired
    private RedisTemplate<byte[], byte[]> redisTemplate;

    @Override
    public Boolean set(final String key, final Object value) {
        return redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
                try {
                    byte[] keyName = KryoUtil.writeToByteArray(key);
                    byte[] val = KryoUtil.writeToByteArray(value);
                    redisConnection.set(keyName, val);
                } catch (Exception e) {
                    log.error(e, e);
                    return false;
                }
                return true;
            }
        });
    }

    @Override
    public <T> T get(final String key, final Class<T> clazz) {
        return redisTemplate.execute(new RedisCallback<T>() {
            @Override
            public T doInRedis(RedisConnection redisConnection) throws DataAccessException {
                try {
                    byte[] keyName = KryoUtil.writeToByteArray(key);
                    byte[] value = redisConnection.get(keyName);
                    if (value == null) {
                        return null;
                    }
                    return KryoUtil.readFromByteArray(value);
                } catch (Exception e) {
                    log.error(e, e);
                    return null;
                }
            }
        });
    }

    @Override
    public Boolean hSet(final String key, final String field, final Object value) {
        return redisTemplate.execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
                try {
                    byte[] keyName = KryoUtil.writeToByteArray(key);
                    byte[] fieldName = KryoUtil.writeToByteArray(field);
                    byte[] val = KryoUtil.writeToByteArray(value);
                    return redisConnection.hSet(keyName, fieldName, val);
                } catch (Exception e) {
                    log.error(e, e);
                    return false;
                }
            }
        });
    }

    @Override
    public <T> T hGet(final String key, final String field, final Class<T> clazz) {
        return redisTemplate.execute(new RedisCallback<T>() {
            @Override
            public T doInRedis(RedisConnection redisConnection) throws DataAccessException {
                try {
                    byte[] keyName = KryoUtil.writeToByteArray(key);
                    byte[] fieldName = KryoUtil.writeToByteArray(field);
                    byte[] value = redisConnection.hGet(keyName, fieldName);
                    if (value == null) {
                        return null;
                    }
                    return KryoUtil.readFromByteArray(value);
                } catch (Exception e) {
                    log.error(e, e);
                    return null;
                }
            }
        });
    }
    @Override
    public Long hLen(final String key) {
        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
                try {
                    byte[] keyName = KryoUtil.writeObjectToByteArray(key);
                    return redisConnection.hLen(keyName);
                } catch (Exception e) {
                    log.error(e,e);
                    return 0L;
                }
            }
        });
    }
    @Override
    public Long hDel(final String key, final String field) {
        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
                try {
                    byte[] keyName = KryoUtil.writeObjectToByteArray(key);
                    byte[] fieldName = KryoUtil.writeObjectToByteArray(field);
                    return redisConnection.hDel(keyName,fieldName);
                } catch (Exception e) {
                    log.error(e,e);
                    return 0L;
                }
            }
        });
    }
    @Override
    public <K, V> Map<K, V> hGetAll(final String key, final Class<K> kClazz, final Class<V> vClazz) {
        return redisTemplate.execute(new RedisCallback<Map<K, V>>() {
            @Override
            public Map<K, V> doInRedis(RedisConnection redisConnection) throws DataAccessException {
                try {
                    byte[] keyName = KryoUtil.writeObjectToByteArray(key);
                    Map<byte[], byte[]> byteMap = redisConnection.hGetAll(keyName);
                    if (byteMap == null) {
                        return null;
                    }
                    Map<K, V> map = new HashMap<K, V>();
                    Set<Map.Entry<byte[], byte[]>> entrySet = byteMap.entrySet();
                    Iterator<Map.Entry<byte[], byte[]>> iter = entrySet.iterator();
                    while (iter.hasNext()) {
                        Map.Entry<byte[], byte[]> entry = iter.next();
                        K k = KryoUtil.readObjectFromByteArray(entry.getKey(), kClazz);
                        V v = KryoUtil.readObjectFromByteArray(entry.getValue(), vClazz);
                        map.put(k, v);
                    }
                    return map;
                } catch (Exception e) {
                    log.error(e,e);
                    return null;
                }
            }
        });
    }

    @Override
    public Boolean doCallBack(RedisTemplateCallBack callBack) {
        return callBack.doCallBack(redisTemplate);
    }
}
